﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace FA.Library.Util
{
    /// <summary>
    /// Provides a basic implementation of the ICloneable interface.
    /// </summary>
    public abstract class BaseCloneable : ICloneable
    {
        /// <summary>
        /// The object will create a clone of itself including all of it's fields.
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            // Create a clone of this instance
            object clone = Activator.CreateInstance(this.GetType());

            // Iterate through all of the fields
            FieldInfo[] fields = this.GetType().GetFields();
            foreach (FieldInfo field in fields)
            {
                // Get the value of the field on this instance
                object value = field.GetValue(this);

                // Clone the value if possible
                if (value != null && value.GetType().GetInterface("ICloneable") != null)
                {
                    value = (value as ICloneable).Clone();
                }

                // Set the value on the cloned object
                field.SetValue(clone, value);
            }

            // Iterate through all of the properties
            PropertyInfo[] properties = this.GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                MethodInfo getMethod = property.GetGetMethod(true);
                MethodInfo setMethod = property.GetSetMethod(true);

                if (getMethod != null && setMethod != null)
                {
                    // Get the value of the field on this instance
                    object value = getMethod.Invoke(this, null);

                    // Clone the value if possible
                    if (value != null && value.GetType().GetInterface("ICloneable") != null)
                    {
                        value = (value as ICloneable).Clone();
                    }

                    // Set the value on the cloned object
                    setMethod.Invoke(clone, new object[] { value });
                }
            }
            return clone;
        }
    }
}
